use crate::config::app_error::AppResult;
use crate::extension::index::index::index_entry_operator::IndexEntryOperator;
use crate::extension::index::index::index_factory::IndexerFactory;
use crate::extension::index::index::indexer::{DefaultIndexer, Indexer};
use crate::extension::index::index::query::index_entry::IndexEntry;
use crate::extension::index::index::query::query::{query_factory, BaseQuery, QueryIndexView};
use crate::extension::index::index::query::{index_spec, query};
use crate::extension::index::page_request::{PageRequest, PageRequestImpl, Sort};
use crate::extension::index::router::list_options::ListOptions;
use crate::extension::index::router::list_request::ListRequest;
use crate::extension::index::router::list_result::ListResult;
use crate::extension::index::selector::label_selector::{SelectorMatcher, SelectorMatchers};
use crate::utils::stopwatch::StopWatch;
use crate::utils::str_util;
use halo_model::{ExtensionOperator, GroupVersionKind, GVK};
use log::trace;
use ordermap::OrderSet;
use rand::seq::index::IndexVec;
use salvo::extract::metadata;
use std::cell::RefCell;
use std::collections::HashMap;
use std::hash::Hash;
use std::sync::Arc;

pub fn retrieve<T: ExtensionOperator + GVK>(
    group_type: &GroupVersionKind,
    options: &ListOptions,
    page: &PageRequestImpl,
) -> AppResult<ListResult<String>> {
    let all_match_result = do_retrieve::<T>(&group_type, options, page.get_sort())?;
    let len = all_match_result.len();
    let vec = ListResult::sub_list(
        all_match_result,
        page.get_page_number(),
        page.get_page_size(),
    );
    Ok(ListResult::new(
        page.get_page_number(),
        page.get_page_size(),
        len,
        vec,
    ))
}

pub fn retrieve_all<T: ExtensionOperator + GVK>(
    group_type: &GroupVersionKind,
    options: &ListOptions,
    sort: &Sort,
) -> AppResult<Vec<String>> {
    do_retrieve::<T>(group_type, options, sort)
}

fn do_retrieve<T: ExtensionOperator + GVK>(
    group_type: &GroupVersionKind,
    options: &ListOptions,
    sort: &Sort,
) -> AppResult<Vec<String>> {
    let indexer = get_index_factory::<T>().get_indexer(group_type)?;
    let mut stop_watch = StopWatch::new(&group_type.to_string());
    stop_watch.start_with_name("Check index status to ensure all indexes are ready");

    let field_names = get_field_names_used_in_list_options::<T>(options, sort);
    check_index_for_names(&indexer, field_names);
    stop_watch.stop();
    let index_view = QueryIndexView::new(Arc::clone(&indexer))?;
    stop_watch.start_with_name("Evaluate selectors for index");
    let result_set = evaluate_selectors_for_index(&indexer, &index_view, &options)?;
    stop_watch.stop();

    stop_watch.start_with_name("Sort result set by sort order");
    let result = index_view.sort_by(result_set, &sort)?;
    stop_watch.stop();

    trace!(
        "Retrieve result from indexer by query [{}],\n {}",
        options,
        stop_watch.pretty_print()
    );

    Ok(result)
}

fn evaluate_selectors_for_index<T: ExtensionOperator + GVK>(
    indexer: &Arc<RefCell<DefaultIndexer>>,
    index_view: &QueryIndexView<DefaultIndexer>,
    options: &ListOptions,
) -> AppResult<OrderSet<String>> {
    let label_selector = &options.label_selector;
    let has_label_selector = label_selector.matchers.is_empty();
    let has_field_selector = true; // options.field_selector.query;
    if !has_field_selector && !has_label_selector {
        return query_factory::all().matches(index_view);
    }

    // only label selector
    if has_label_selector && !has_field_selector {
        let vec = &label_selector.matchers;
        return retrieve_for_label_matchers(indexer, vec);
    }

    if !has_label_selector {
        let field_selector = &options.field_selector.query;
        return field_selector.matches(index_view);
    }

    let field_selector = &options.field_selector;

    let for_field = field_selector.query.matches(index_view)?;
    let matchers = &label_selector.matchers;

    let for_label = retrieve_for_label_matchers(indexer, matchers)?;

    // var resultSet = (forField.size() <= forLabel.size()) ? forField : forLabel;
    // resultSet.retainAll((resultSet == forField) ? forLabel : forField);

    let result_set = for_field
        .intersection(&for_label)
        .cloned()
        .collect::<OrderSet<_>>();

    Ok(result_set)
}

fn retrieve_for_label_matchers<T: ExtensionOperator + GVK>(
    indexer: &Arc<RefCell<DefaultIndexer>>,
    matcher: &Vec<SelectorMatchers>,
) -> AppResult<OrderSet<String>> {
    let object_name_labels_map = build_from(indexer, matcher)?;

    // O(k×m) time complexity, k is the number of keys, m is the number of labelMatchers
    let mut set = OrderSet::new();
    for (object_name, labels) in object_name_labels_map {
        if matcher
            .iter()
            .all(|it| it.test(str_util::option_str(labels.get(&it.get_key()))))
        {
            set.insert(object_name);
        }
    }

    Ok(set)
}

type ObjectLabelMap = HashMap<String, HashMap<String, String>>;

fn build_from<T: ExtensionOperator + GVK>(
    indexer: &Arc<RefCell<DefaultIndexer>>,
    matcher: &Vec<SelectorMatchers>,
) -> AppResult<ObjectLabelMap> {
    let mut object_name_labels_map = ObjectLabelMap::new();

    let index_entry = indexer.borrow().get_index_entry("metadata.labels")?;

    let mut label_keys = matcher.iter().map(|it| it.get_key()).collect::<Vec<_>>();
    label_keys.sort();

    for (key, value) in index_entry.borrow().entries() {
        let (first, second) = index_spec::label_key_value_pair(key)?;

        if !label_keys.contains(&first) {
            continue;
        }

        if !object_name_labels_map.contains_key(value) {
            let mut map = HashMap::new();
            map.insert(first, second);
            object_name_labels_map.insert(value.clone(), map);
        }
    }
    let index_entry = indexer
        .borrow()
        .get_index_entry(index_spec::PRIMARY_INDEX_NAME)?;
    let name_index_operator = IndexEntryOperator::new(Arc::clone(&index_entry));
    let mut all_indexed_object_names = name_index_operator.get_values();

    object_name_labels_map.keys().for_each(|name| {
        all_indexed_object_names.remove(name);
    });
    all_indexed_object_names.iter().for_each(|name| {
        object_name_labels_map.insert(name.clone(), HashMap::new());
    });
    Ok(object_name_labels_map)
}

fn get_index_factory<'a, T: ExtensionOperator + halo_model::GVK>() -> &'a IndexerFactory {
    // IndexerFactory<T: ExtensionOperator>
    // let list = Vec::new();

    todo!()
}

fn get_field_names_used_in_list_options<T: ExtensionOperator>(
    options: &ListOptions,
    sort: &Sort,
) -> OrderSet<String> {
    let mut set = OrderSet::new();
    set.insert(index_spec::PRIMARY_INDEX_NAME.to_string());
    for order in &sort.orders {
        set.insert(order.property.clone());
    }
    let query = &options.field_selector.query;
    let field_names = query::get_field_names_used_in_query(query);

    set.extend(field_names);
    set
}

fn check_index_for_names<T: ExtensionOperator + GVK>(
    indexer: &Arc<RefCell<DefaultIndexer>>,
    index_names: OrderSet<String>,
) -> AppResult<()> {
    for index_name in &index_names {
        if let Err(e) = indexer.borrow().get_index_entry(index_name) {
            break;
        };
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use crate::extension::index::index::query::index_spec::{LABEL_PATH, PRIMARY_INDEX_NAME};
    use crate::extension::index::index::query::query::equal::Equal;
    use crate::extension::index::index::query::query::query_factory::Query::Equal as EqualQuery;
    use crate::extension::index::page_request::Sort;
    use crate::extension::index::router::list_options::ListOptions;
    use crate::extension::index::selector::field_selector::FieldSelector;
    use crate::extension::index::selector::label_selector::LabelSelectorBuilder;
    use crate::tests::extension::DemoExtension;
    use crate::tests::index_view_data_set::{pile_for_indexer, MockIndexEntry, MockIndexer};
    use halo_model::GroupVersionKind;
    use std::cell::RefCell;
    use std::collections::HashMap;
    use std::sync::Arc;

    #[test]
    fn do_retrieve() {
        let mut index_entry: HashMap<String, Arc<RefCell<MockIndexEntry<DemoExtension>>>> =
            HashMap::new();

        pile_for_indexer(
            &mut index_entry,
            PRIMARY_INDEX_NAME.to_string(),
            vec![
                ("object1".to_string(), "object1".to_string()),
                ("object2".to_string(), "object2".to_string()),
                ("object3".to_string(), "object3".to_string()),
            ],
        );

        pile_for_indexer(
            &mut index_entry,
            LABEL_PATH.to_string(),
            vec![
                ("key1=value1".to_string(), "object1".to_string()),
                ("key2=value2".to_string(), "object1".to_string()),
                ("key1=value1".to_string(), "object3".to_string()),
                ("key2=value2".to_string(), "object3".to_string()),
                ("key1=value1".to_string(), "object3".to_string()),
            ],
        );
        pile_for_indexer(
            &mut index_entry,
            "slug".to_string(),
            vec![
                ("slug1".to_string(), "object1".to_string()),
                ("slug2".to_string(), "object2".to_string()),
            ],
        );
        let mut mock_indexer = MockIndexer::mock_with(index_entry);

        let label_selector = LabelSelectorBuilder::builder().eq("key1", "value1").build();
        let field_selector = FieldSelector::of(EqualQuery(Equal::new(
            "slug".to_string(),
            "slug1".to_string(),
        )));
        let list_options = ListOptions::new(label_selector, field_selector);

        let kind = GroupVersionKind {
            group: "test".to_string(),
            version: "v1".to_string(),
            kind: "demo".to_string(),
        };
        let result = super::do_retrieve::<DemoExtension>(&kind, &list_options, &Sort::unsorted());
    }
}
